package com.xzq.erp.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.xzq.erp.context.BaseContext;
import com.xzq.erp.domain.dto.MenuDTO;
import com.xzq.erp.domain.po.Menu;
import com.xzq.erp.domain.po.SysPermission;
import com.xzq.erp.domain.po.SysUser;
import com.xzq.erp.domain.query.MenuPageQuery;
import com.xzq.erp.domain.vo.MenuVO;
import com.xzq.erp.enums.MenuStatus;
import com.xzq.erp.enums.MenuType;
import com.xzq.erp.enums.ResponseCodeEnum;
import com.xzq.erp.exception.BizException;
import com.xzq.erp.mapper.MenuMapper;
import com.xzq.erp.service.IMenuService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xzq.erp.service.ISysPermissionService;
import com.xzq.erp.service.ISysUserService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 系统菜单表 服务实现类
 * </p>
 *
 * @author xzq
 * @since 2025-05-13
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class MenuServiceImpl extends ServiceImpl<MenuMapper, Menu> implements IMenuService {


    private final ISysPermissionService sysPermissionService;

    private final ISysUserService sysUserService;

    @Override
    public List<MenuVO> getMenuList(MenuPageQuery query) {
        log.info("用户ID:{}", BaseContext.getUserId());
        List<Menu> menus = lambdaQuery()
                .like(StrUtil.isNotBlank(query.getName()), Menu::getLabel, query.getName())
                .eq(Objects.nonNull(query.getStatus()),Menu::getIsVisible,  Objects.nonNull(query.getStatus()) && query.getStatus() == 1)
                .orderByAsc(Menu::getSort) // 排序
                .orderByAsc(Menu::getPid)// 排序
                .list();
        return buildMenuTree(menus, true);
    }

    @Override
    public List<MenuVO> getCurrentUserMenuList() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        if (authentication == null || !authentication.isAuthenticated()) {
            return new ArrayList<>();
        }
        
        Object principal = authentication.getPrincipal();
        if (principal instanceof UserDetails) {
            UserDetails userDetails = (UserDetails) principal;
            Collection<? extends GrantedAuthority> authorities = userDetails.getAuthorities();
            boolean isAdmin = false;
            for(GrantedAuthority authority : authorities){
                if("ROLE_ADMIN".equals(authority.getAuthority())){
                    isAdmin = true;
                    break;
                }
            }
            SysUser user = getUserByUsername(userDetails.getUsername());
            if (user != null && !isAdmin) {
                return getMenuListByUserId(user.getId(),false);
            }
            if(user != null){
                return getMenuList(new MenuPageQuery());
            }
        }
        
        return new ArrayList<>();
    }

    @Override
    public List<MenuVO> getMenuListByUserId(Integer userId, Boolean isMenu) {
        List<Menu> menus = baseMapper.selectMenusByUserId(userId);
        return buildMenuTree(menus,isMenu);
    }

    @Override
    public List<MenuVO> getMenuListByRoleId(Integer roleId) {
        List<Menu> menus = baseMapper.selectMenusByRoleId(roleId);
        return buildMenuTree(menus, false);
    }

    @Override
    @Transactional
    public void save(MenuDTO menuDTO) {
        if(Objects.isNull(menuDTO.getType())){
            throw new BizException(ResponseCodeEnum.MENU_TYPE_NOT_EXIST);
        }
        // 判断是目录、菜单还是权限
        Long parentId = menuDTO.getParentId();
        MenuType type = menuDTO.getType();
        Menu menu = BeanUtil.copyProperties(menuDTO, Menu.class);
        menu.setPid(parentId.intValue());
        SysPermission sysPermission = new SysPermission();
        sysPermission.setName(menuDTO.getPermission());
        sysPermission.setLabel(menuDTO.getLabel());
        switch(type) {
            case DIR:
                if(StrUtil.isBlank(menuDTO.getPath())){
                    throw new BizException(ResponseCodeEnum.PARAM_NOT_VALID);
                }
                save(menu);
                break;
            case MENU:
                if(StrUtil.isBlank(menuDTO.getPath())){
                    throw new BizException(ResponseCodeEnum.PARAM_NOT_VALID);
                }
                save(menu);
                sysPermission.setMenuId(menu.getId());
                sysPermission.setType(MenuType.MENU.getValue());
                sysPermissionService.save(sysPermission);
                break;
            case BUTTON:
                sysPermission.setMenuId(menuDTO.getParentId().intValue());
                sysPermission.setType(MenuType.BUTTON.getValue());
                sysPermissionService.save(sysPermission);
                break;
        }
    }

    @Override
    public void updateMenu(MenuDTO menuDTO) {
        if(Objects.isNull(menuDTO.getId())){
            throw new BizException(ResponseCodeEnum.PARAM_NOT_VALID);
        }
        if(Objects.isNull(menuDTO.getType())){
            throw new BizException(ResponseCodeEnum.MENU_TYPE_NOT_EXIST);
        }
        // 判断是目录、菜单还是权限
        Long parentId = menuDTO.getParentId();
        MenuType type = menuDTO.getType();
        Menu menu = BeanUtil.copyProperties(menuDTO, Menu.class);
        menu.setId(menuDTO.getId().intValue());
        menu.setPid(parentId.intValue());
        SysPermission sysPermission = new SysPermission();
        if(menuDTO.getType() == MenuType.BUTTON){
            sysPermission.setId(menuDTO.getId().intValue());
        }
        sysPermission.setName(menuDTO.getPermission());
        sysPermission.setLabel(menuDTO.getLabel());
        switch(type) {
            case DIR:
                // 目录
                if(menuDTO.getType() == menuDTO.getOldType()){
                    if(StrUtil.isBlank(menuDTO.getPath())){
                        throw new BizException(ResponseCodeEnum.PARAM_NOT_VALID);
                    }
                    updateById(menu);
                }
                // 菜单 -> 目录
                else if(menuDTO.getOldType() == MenuType.MENU){
                    updateById(menu);
                    sysPermissionService.remove(new LambdaQueryWrapper<SysPermission>().eq(SysPermission::getName, menuDTO.getPermission()));
                }
                // 按钮 -> 目录
                else if(menuDTO.getOldType() == MenuType.BUTTON){
                    // 先创建菜单
                    save(menu);
                    // 再删除权限
                    sysPermissionService.remove(new LambdaQueryWrapper<SysPermission>().eq(SysPermission::getName, menuDTO.getPermission()));
                }
                break;
            case MENU:
                if(menuDTO.getType() == menuDTO.getOldType()){
                    if(StrUtil.isBlank(menuDTO.getPath())){
                        throw new BizException(ResponseCodeEnum.PARAM_NOT_VALID);
                    }
                    updateById(menu);
                    boolean success = sysPermissionService.lambdaUpdate()
                            .eq(SysPermission::getName, menuDTO.getPermission())
                            .set(SysPermission::getMenuId, menuDTO.getParentId().intValue())
                            .set(SysPermission::getType, MenuType.MENU.getValue())
                            .update();
                    if(!success){
                        sysPermission.setMenuId(menuDTO.getParentId().intValue());
                        sysPermission.setType(MenuType.MENU.getValue());
                        sysPermissionService.save(sysPermission);
                    }
                }
                // 目录 -> 菜单
                else if(menuDTO.getOldType() == MenuType.DIR){
                    if(StrUtil.isBlank(menuDTO.getPath())){
                        throw new BizException(ResponseCodeEnum.PARAM_NOT_VALID);
                    }
                    updateById(menu);
                    sysPermission.setMenuId(menuDTO.getId().intValue());
                    sysPermission.setType(MenuType.MENU.getValue());
                    sysPermissionService.save(sysPermission);
                }
                // 按钮 -> 菜单
                else if(menuDTO.getOldType() == MenuType.BUTTON){
                    // 先创建菜单
                    save(menu);
                    // 再更新关联的权限
                    sysPermissionService.lambdaUpdate()
                            .eq(SysPermission::getName, menuDTO.getPermission())
                            .set(SysPermission::getMenuId, menuDTO.getParentId().intValue())
                            .set(SysPermission::getType, MenuType.MENU.getValue())
                            .update();
                }
                break;
            case BUTTON:
                if(menuDTO.getType() == menuDTO.getOldType()){
                    sysPermission.setMenuId(menuDTO.getParentId().intValue());
                    sysPermission.setType(MenuType.BUTTON.getValue());
                    sysPermissionService.updateById(sysPermission);
                }
                // 目录 -> 按钮
                else if(menuDTO.getOldType() == MenuType.DIR){
                    // 先创建权限
                    sysPermission.setMenuId(menuDTO.getParentId().intValue());
                    sysPermission.setType(MenuType.BUTTON.getValue());
                    sysPermissionService.save(sysPermission);
                    // 再删除菜单
                    removeById(menuDTO.getId());
                }
                // 菜单 -> 按钮
                else if(menuDTO.getOldType() == MenuType.MENU){
                    // 先创建/更新权限
                    sysPermission.setMenuId(menuDTO.getParentId().intValue());
                    sysPermission.setType(MenuType.BUTTON.getValue());

                    boolean exists = sysPermissionService.lambdaQuery()
                            .eq(SysPermission::getName, menuDTO.getPermission())
                            .exists();

                    if(exists) {
                        sysPermissionService.lambdaUpdate()
                                .eq(SysPermission::getName, menuDTO.getPermission())
                                .set(SysPermission::getType, MenuType.BUTTON.getValue())
                                .set(SysPermission::getMenuId, menuDTO.getParentId().intValue())
                                .update();
                    } else {
                        sysPermissionService.save(sysPermission);
                    }

                    // 再删除菜单
                    removeById(menuDTO.getId());
                }
                break;
        }
    }

    @Override
    public void deleteMenu(Integer id,Integer type) {
        // 判断删除的菜单类型
        switch (MenuType.of(type)) {
            case DIR:
                // 目录
                removeById(id);
                break;
            case MENU:
                // 菜单
                removeById(id);
                sysPermissionService.remove(new LambdaQueryWrapper<SysPermission>().eq(SysPermission::getMenuId, id));
                break;
            case BUTTON:
                // 按钮
                sysPermissionService.removeById(id);
                break;
        }
    }

    /**
     * 构建菜单树
     *
     * @param menus  菜单列表
     * @param isMenu
     * @return 菜单树
     */
    private List<MenuVO> buildMenuTree(List<Menu> menus, Boolean isMenu) {
        List<MenuVO> menuVOS = new ArrayList<>();
        List<Integer> menuIds = menus.stream().map(Menu::getId).collect(Collectors.toList());
        if (menus == null || menus.isEmpty() || menuIds.isEmpty()) {
            return menuVOS;
        }
        // 获取所有权限
        List<SysPermission> sysPermissionList = sysPermissionService.lambdaQuery()
                .in(SysPermission::getMenuId, menuIds)
                .list();
        // 权限按菜单ID分组，并按type排序
        Map<Integer, List<SysPermission>> permissionMap = new LinkedHashMap<>();
        sysPermissionList.stream()
                .sorted(Comparator.comparing(SysPermission::getType)) // type 小的在前
                .forEach(p -> permissionMap.computeIfAbsent(p.getMenuId(), k -> new ArrayList<>()).add(p));
        // 菜单映射，便于快速查找
        Map<Integer, Menu> menuMap = menus.stream()
                .collect(Collectors.toMap(Menu::getId, menu -> menu, (a, b) -> a));
        // 顶级菜单映射
        Map<Integer, MenuVO> rootMenuVOMap = new HashMap<>();
        // 首先处理所有菜单，转换为MenuVO
        for (Menu menu : menus) {
            MenuVO menuVO = BeanUtil.toBean(menu, MenuVO.class);
            menuVO.setStatus(MenuStatus.of(menu.getIsVisible() ? 1 : 0));
            // 设置菜单权限标识（如果有）
            if (permissionMap.containsKey(menu.getId()) && !permissionMap.get(menu.getId()).isEmpty()) {
                menuVO.setPermission(permissionMap.get(menu.getId()).get(0).getName());
            }
            if(isMenu){
                // 为菜单添加权限子项
                List<MenuVO> permissionChildren = createPermissionChildren(menu.getId(), permissionMap);
                menuVO.setChildren(permissionChildren);
            }
            if (menu.getPid() == 0) {
                // 顶级菜单直接添加到结果集
                rootMenuVOMap.put(menu.getId(), menuVO);
                menuVOS.add(menuVO);
            }
        }

        //处理父子关系
        for (Menu menu : menus) {
            if (menu.getPid() != 0 && rootMenuVOMap.containsKey(menu.getPid())) {
                // 找到父菜单
                MenuVO parentMenuVO = rootMenuVOMap.get(menu.getPid());
                // 获取或初始化父菜单的子菜单列表
                List<MenuVO> children = parentMenuVO.getChildren();
                if (children == null) {
                    children = new ArrayList<>();
                    parentMenuVO.setChildren(children);
                }
                // 获取当前菜单的MenuVO
                MenuVO currentMenuVO = BeanUtil.toBean(menu, MenuVO.class);
                currentMenuVO.setStatus(MenuStatus.of(menu.getIsVisible() ? 1 : 0));
                // 设置菜单权限标识（如果有）
                if (permissionMap.containsKey(menu.getId()) && !permissionMap.get(menu.getId()).isEmpty()) {
                    currentMenuVO.setPermission(permissionMap.get(menu.getId()).get(0).getName());
                }
                // 为当前菜单添加权限子项
                List<MenuVO> permissionChildren = createPermissionChildren(menu.getId(), permissionMap);
                currentMenuVO.setChildren(permissionChildren);
                // 将当前菜单添加为父菜单的子菜单
                children.add(currentMenuVO);
            }
        }

        return menuVOS;
    }

    // 创建权限子项
    private List<MenuVO> createPermissionChildren(Integer menuId, Map<Integer, List<SysPermission>> permissionMap) {
        List<MenuVO> permissionChildren = new ArrayList<>();
        if (permissionMap.containsKey(menuId)) {
                int i = 1; // 排序计数器
                // 遍历该菜单下的所有权限
                for (SysPermission permission : permissionMap.get(menuId)) {
                    // 只添加type不为1的权限作为子项
                    if (permission.getType() != 1) {
                        MenuVO permissionVO = new MenuVO();
                        permissionVO.setId(permission.getId());
                        permissionVO.setLabel(permission.getName());
                        permissionVO.setPermission(permission.getName());
                        permissionVO.setStatus(MenuStatus.of(1)); // 默认启用
                        permissionVO.setSort(i++);
                        permissionVO.setCreateTime(permission.getCreateTime());
                        permissionChildren.add(permissionVO);
                    }
                }
            }
            return permissionChildren;
        }
    
    /**
     * 根据用户名获取用户
     * @param username 用户名
     * @return 用户对象
     */
    private SysUser getUserByUsername(String username) {
        return sysUserService.lambdaQuery()
                .eq(SysUser::getUsername, username)
                .one();
    }
}
